home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Development Platforms / Apple II / Essentials / Technical.Notes / IIGS / TN.IIGS.075 < prev    next >
Encoding:
Text File  |  1992-07-15  |  10.3 KB  |  201 lines  |  [TEXT/GEOL]

  1. Apple II
  2. Technical Notes
  3. _____________________________________________________________________________
  4.                                                   Developer Technical Support
  5. Apple IIgs
  6. #75: BeginUpdate Anomaly
  7.  
  8. Revised by: Dave Lyons                                               May 1992
  9. Written by: Eric Soldan                                          January 1990
  10.  
  11. This Technical Note discusses a Window Manager anomaly with the handling of
  12. the visRgn and the updateRgn between BeginUpdate and EndUpdate calls.
  13.  
  14. CHANGES SINCE JANUARY 1990:  Updated for System 6.0.  CopyPixels is in a
  15. static segment, and GS/OS automatically prompts for disks on the text screen
  16. when necessary to avoid interfering with a window update in progress.
  17. _____________________________________________________________________________
  18.  
  19.  
  20. If an application calls BeginUpdate, it needs to be fully aware of what is
  21. going on behind the scenes in terms of its visRgn and updateRgn.  Typically an
  22. application has TaskMaster handle the update events.  TaskMaster calls
  23. BeginUpdate, the application update procedure, then EndUpdate.  So any
  24. application that uses TaskMaster to handle updates, whether or not it makes
  25. any BeginUpdate calls directly, needs to be aware of problem described in this
  26. Note.
  27.  
  28. BeginUpdate is responsible for intersecting the visRgn and the updateRgn and
  29. making the intersection of these two regions the temporary visRgn.  (EndUpdate
  30. undoes this effect.)  Following are the steps BeginUpdate takes to do this:
  31.  
  32.     1. Localize the updateRgn.  (All grafPort regions are local,
  33.        therefore the visRgn is local.  All window regions are global,
  34.        therefore the updateRgn is global.  One of them has to change if
  35.        they are to be intersected correctly.)
  36.     2. Intersect the visRgn and localized updateRgn, then place the
  37.        result in the updateRgn.
  38.     3. Swap the visRgn and updateRgn handles.
  39.  
  40.        The handle swapping has two effects:
  41.  
  42.        o   Makes the intersection region the current visRgn.
  43.        o   Saves the real visRgn as the updateRgn.  (Saving the real
  44.            visRgn is necessary because everything has to be restored to
  45.            normal by EndUpdate.)
  46.  
  47. EndUpdate restores things to normal after an update procedure is finished.
  48. When an application calls EndUpdate, it swaps back the handles and sets the
  49. updateRgn to empty.
  50.  
  51.  
  52. SO WHAT'S THE PROBLEM?
  53.  
  54. The problem is that the updateRgn is not a very good place to save the visRgn.
  55. Since InvalRect and InvalRgn modify the updateRgn, if either of these two
  56. calls is made between a BeginUpdate and EndUpdate, they modify the saved
  57. visRgn.  When the update is finished, EndUpdate restores the modified visRgn
  58. instead of the original.
  59.  
  60. The solution to this problem seems simple enough:  don't call InvalRect or
  61. InvalRgn between BeginUpdate and EndUpdate.  Unfortunately, there are other
  62. calls which can call BeginUpdate, EndUpdate, InvalRect, and InvalRgn, so an
  63. application might inadvertently call one of these routines.
  64.  
  65. If this situation isn't bad enough already, you could really mess things up by
  66. opening another window between BeginUpdate and EndUpdate calls.  Opening a
  67. window at this time may seem like a perfectly normal thing (i.e., to display
  68. an alert); however, opening a window forces the recalculation of the visRgn
  69. for any windows obscured by the new window.  If the window being updated has
  70. its visRgn recalculated, the application obviously loses the visRgn that
  71. BeginUpdate created.  This doesn't seem too serious since the visRgn is
  72. restored to the entire visible part of the window when the new window is
  73. closed; however, it does mean that the application would have to update the
  74. entire window instead of the original updateRgn.
  75.  
  76. Unfortunately, the Window Manager also posts update events for the portion of
  77. the window that was obscured, and it does this by changing the updateRgn.  Of
  78. course the updateRgn for the window being updated is really the visRgn that is
  79. being "safely" preserved until the EndUpdate call.  So, there are some really
  80. good reasons why this can't be done.
  81.  
  82. Okay, so along with not making calls to InvalRect and InvalRgn between
  83. BeginUpdate and EndUpdate, an application cannot open any other windows
  84. either.  Good.
  85.  
  86. NOW TO MAKE THINGS EVEN WORSE.
  87.  
  88. Starting with System 5.0, some toolbox functions are stored on disk in dynamic
  89. segments and loaded when they are first called.  For example, CopyPixels is in
  90. a dynamic segment in System versions 5.0 through 5.0.3.  If the startup disk
  91. is not available and the system prompts for it between BeginUpdate and
  92. EndUpdate by calling AlertWindow, the bad things discussed above happen.
  93.  
  94. Starting with System 6.0, the system is smart enough not to prompt for a disk
  95. using AlertWindow if a window update is in progress.  (Internally, GS/OS calls
  96. WindStatus to see if it can prompt on the graphics screen.  If BeginUpdate has
  97. been called more times than EndUpdate, WindStatus fibs by returning with the
  98. carry set.  GS/OS takes the hint and prompts for the disk with a text dialog
  99. instead.)
  100. But I Have to Do...
  101.  
  102. If you absolutely must do some of the things previously discussed, there is a
  103. way to accomplish it.  It is not simple, but it can be done.
  104.  
  105. Assuming that BeginUpdate has been called, and an application is in its update
  106. procedure:
  107.  
  108.     1. Create a new region and copy the visRgn into it.  Doing this
  109.        allows the application to restore the visRgn to just the area to
  110.        be updated that BeginUpdate calculated.  This needs to be done
  111.        for any other windows which obscure a part the the window being
  112.        updated.  Again, these are not windows that an application would
  113.        open directly.  CopyPixels may open a window, since it is a
  114.        dynamic segment and may need to get loaded from a disk that is
  115.        off-line.
  116.     2. Create a new region, then swap its handle with the updateRgn
  117.        handle.  This protects the real visRgn and lets an application
  118.        call InvalRect and InvalRgn at any time if necessary.  It also
  119.        means the application doesn't need to worry about the system
  120.        making these calls either.  The updateRgn is also an empty region
  121.        after the swap, so any contributions to it constitute a valid
  122.        update event that needs to be handled.
  123.     3. Do the update part of the update procedure.  In this part, if the
  124.        application has any calls to CopyPixels, or any other QuickDraw
  125.        Auxiliary dynamic segment functions, after the call is completed,
  126.        copy the saved visRgn back to the visRgn of the grafPort.  The
  127.        closing of the dynamic segment alert window recalculates the
  128.        visRgn, and copying it undoes this effect.  Do not do the same
  129.        for the updateRgn.  Leave the updateRgn alone.  We are
  130.        accumulating an actual updateRgn, and the closing of the alert
  131.        window for the dynamic segment may have contributed to this
  132.        region.
  133.  
  134. There are two methods for leaving the update procedure.  Although the second
  135. method works whether or not an application uses TaskMaster, if an application
  136. does not use TaskMaster, then the first method is simpler.
  137.  
  138. The procedure without using TaskMaster (i.e., you made the BeginUpdate call,
  139. and you will make the EndUpdate call) is as follows:
  140.  
  141.     A. Dispose of the region created in Step 1.  This region was only
  142.        needed to restore the partial visRgn that BeginUpdate calculated
  143.        after a window was opened.
  144.     B. Swap the updateRgn handle with the region handle created in Step
  145.        2.
  146.     C. Make the EndUpdate call.
  147.     D. If the region created in Step 2 is not empty, copy this region
  148.        into the updateRgn for the window with CopyRgn.  You can't just
  149.        do an InvalRgn with it because InvalRgn globalizes the region and
  150.        the region is already global.  You want to copy the region since
  151.        this generates a valid update event.  You can use CopyRgn instead
  152.        of UnionRgn because the update region is empty.
  153.     E. Dispose of the region created in Step 2.
  154.  
  155. With TaskMaster, things are a little messier.  Since TaskMaster makes the
  156. EndUpdate call, you have less control over the situation.  It is important to
  157. do the EndUpdate before generating the update event.  Posting the update event
  158. has to happen outside the update procedure, since you have to leave the update
  159. procedure for TaskMaster to do the EndUpdate.  So it follows that you do Steps
  160. A and B, post an application event to handle the rest externally, and when the
  161. application event is handled, do Steps D and E.
  162. Some consideration was given to posting an application event via the PostEvent
  163. call.  Unfortunately, there is a possibility that this application event will
  164. drop out of the queue not handled.  When the queue is full, the oldest event
  165. is dropped, and this could occur to application events, which would be very
  166. bad in this case.  Due to this possibility, posting an application event
  167. refers to setting a global variable that is checked before the TaskMaster call
  168. in the main event loop.  This can be considered equivalent to posting an event
  169. via the PostEvent call.
  170.  
  171. So, the TaskMaster case would be as follows:
  172.  
  173.     A. Dispose of the region created in Step 1.
  174.     B. Swap the updateRgn handle with the region handle created in Step
  175.        2.
  176.     C. Store the handle of the region created in Step 2 in a global
  177.        variable named eventUpdateRgn.  Store the current window port in
  178.        a global variable named eventWindowPort.
  179.     D. Return to TaskMaster, which returns to the main event loop.
  180.     E. Immediately after the TaskMaster call in the main event loop,
  181.        check the global variable eventUpdateRgn.  If it is not NULL
  182.        then:
  183.        a. Copy the region into the updateRgn of the window
  184.           eventWindowPort.  Using CopyRgn is the easiest way to copy the
  185.           region.  (Copying the region posts an update event if the
  186.           event UpdateRgn is not NULL.
  187.        b. Dispose of the region eventUpdateRgn, then set the variable
  188.           eventUpdateRgn to NULL, so that this "event" won't be handled
  189.           again.
  190.  
  191. Of course, the simplest way to handle all of this is to avoid situations where
  192. you have to take the steps described above.  If things like opening a window
  193. (or allowing the system to open one) and InvalRect and InvalRgn can be avoided
  194. between calls to BeginUpdate and EndUpdate, so can all of this ugliness.
  195.  
  196.  
  197. Further Reference
  198. _____________________________________________________________________________
  199.  
  200.    o   Apple IIgs Toolbox Reference, Volume 2
  201.